#ifndef ZML_DP_TEST_H_
#define ZML_DP_TEST_H_

#include "DensityPeakAlgorithm.h"
#include "../Render/RenderOption.h"
#include <fstream>
#include <vector>
#include <Tools\ZLogmanager.h>
#include "../Math/MathTools.h"

GLOBAL_NAMESPACE_BEGIN

namespace MachineLearningTest
{


    class DPTest
    {
    public:
        struct SaveOptions
        {
            bool bData;
            bool bDensity;
            bool bDelta;
            float threshold;
        };

        /*
        -txtFileFolder
        |--txtFile.txt
        -outputFolder
        |--decisionGraph.obj
        |--result.txt
        |--resultFolder
        */

        struct InputOptions
        {
            std::string txtFileFullPath;
            std::string outputFolder;
            std::string resultNamePrefix;
            double dcScale;
            double gapScale;
            bool reCalculateDG;
        };

        enum Attribute {
            XML_ML_TXTDATA_FULLPATH = 0,
            XML_ML_OUTPUT_FOLDER,
            XML_ML_RESULT_PREFIX,
            XML_ML_DC_SCALE,
            XML_ML_GAP_SCALE,
            XML_ML_RECACULATE_DG,
            XML_ML_SKIP
        };

        static const char* InputOptionNames[7];

        static void runTest();
        static void runTest2();
        static void runTest3();

        static void runTest4();

        static void runTests(const char* xmlFileName);

        static void runDPAlgorithm(const char* dataTxtFile);
        static void analyseDPResult(const char* dataTxtFile, const char* resultFile, const char* outputFolder);

        static void getDataFileNames(const char* inFileName, const std::string& folderName, std::vector<std::string>& fileNames);

        static void saveDecisionGraphAsObj(const char* fileName, const SaveOptions& opt, MLAlgorithms::DensityPeak* pDp)
        {
            std::ofstream fout(fileName);
            if (!fout || pDp == NULL)
                return;

            std::vector<double> rhoN = MathTools::normalize(pDp->getRhos());
            std::vector<double> delN = MathTools::normalize(pDp->getDeltas());
            int nSize = pDp->getPointSize();
            for (size_t i = 0; i < nSize; i++)
            {
                if (opt.bDensity && opt.bDelta && rhoN[i] * delN[i] >= opt.threshold)
                {
                    Color col = RenderOption::colorCoding(rhoN[i] * delN[i]);
                    fout << "v "  << rhoN[i] << " " << delN[i] << " " << " 0\n";
                    fout << "vc " << col[0] << " " << col[1] << " " << col[2] << " \n";
                }
                else if (opt.bDensity && rhoN[i] >= opt.threshold)
                {
                    Color col = RenderOption::colorCoding(rhoN[i]);
                    fout << "v "  << rhoN[i] << " " << delN[i] << " " << " 0\n";
                    fout << "vc " << col[0] << " " << col[1] << " " << col[2] << " \n";
                }
                else if (opt.bDelta && delN[i] >= opt.threshold)
                {
                    Color col = RenderOption::colorCoding(delN[i]);
                    fout << "v "  << rhoN[i] << " " << delN[i] << " " << " 0\n";
                    fout << "vc " << col[0] << " " << col[1] << " " << col[2] << " \n";
                }
            }
            fout.close();
        }
        
        static void saveAsObj(const char* fileName, MLAlgorithms::DensityPeak* pDpa)
        {
            std::ofstream fout(fileName);
            if (!fout || !pDpa)
                return;

            Eigen::MatrixXd data = pDpa->getPoints();
            for (int i = 0; i < pDpa->getPointSize(); i++)
            {
                fout << "v " << data(i, 0) << " " << data(i, 1) << " 0\n";
            }

            std::vector<double> deltas = pDpa->getDeltas();
            double maxDelta = *std::max_element(deltas.begin(), deltas.end());
            double minDelta = *std::min_element(deltas.begin(), deltas.end());
            for (int i = 0; i < pDpa->getPointSize(); i++)
            {
                double delta = (deltas[i] - minDelta) / (maxDelta - minDelta);
                Color col = RenderOption::colorCoding(delta);
                fout << "vc " << col[0] << " " << col[1] << " " << col[2] << "\n";
            }

            fout.close();
        }

        static void saveResultAsObj(const char* fileName, MLAlgorithms::DensityPeak* pDpa)
        {
            std::ofstream fout(fileName);
            if (!fout || !pDpa)
                return;

            std::vector<double> rhos = pDpa->getRhos();
            std::vector<double> deltas = pDpa->getDeltas();

            if (rhos.size() != pDpa->getPointSize() || deltas.size() != pDpa->getPointSize())
            {
                fout.close();
                return;
            }

            double maxRho = *std::max_element(rhos.begin(), rhos.end());
            double minRho = *std::min_element(rhos.begin(), rhos.end());
            double maxDelta = *std::max_element(deltas.begin(), deltas.end());
            double minDelta = *std::min_element(deltas.begin(), deltas.end());
            for (int i = 0; i < pDpa->getPointSize(); i++)
            {
                double delta = (deltas[i] - minDelta) / (maxDelta - minDelta);
                double rho = (rhos[i] - minRho) / (maxRho - minRho);
                fout << "v " << rhos[i] << " " << delta << " 0\n";
            }

            fout.close();
        }

        static void analyseAndSaveResultsAsObj(const char* pointFileName, const char* resultFileName, MLAlgorithms::DensityPeak* pDpa, const SaveOptions& opt)
        {
            std::ofstream fout(pointFileName);
            std::ofstream foutRet(resultFileName);
            if (!fout || !pDpa || !foutRet)
                return;

            std::vector<double> rhos = pDpa->getRhos();
            std::vector<double> deltas = pDpa->getDeltas();
            std::vector<double> rhoTimesDelta = pDpa->getRhoTimesDeltas();
            Eigen::MatrixXd data = pDpa->getPoints();

            if (rhos.size() != pDpa->getPointSize() || deltas.size() != pDpa->getPointSize())
            {
                fout.close();
                return;
            }

            double maxRho = *std::max_element(rhos.begin(), rhos.end());
            double minRho = *std::min_element(rhos.begin(), rhos.end());
            double maxDelta = *std::max_element(deltas.begin(), deltas.end());
            double minDelta = *std::min_element(deltas.begin(), deltas.end());
            for (int i = 0; i < pDpa->getPointSize(); i++)
            {
                double delta = (deltas[i] - minDelta) / (maxDelta - minDelta);
                double rho = (rhos[i] - minRho) / (maxRho - minRho);
                if (rhoTimesDelta[i]<opt.threshold) continue;

                fout << "v " << data(i, 0) << " " << data(i, 1) << " 0\n";
                foutRet << "v " << rhos[i] << " " << delta << " 0\n";

                if (opt.bDensity && opt.bDelta)
                {
                    Color col = RenderOption::colorCoding(sqrt(rho*delta));
                    fout << "vc " << col[0] << " " << col[1] << " " << col[2] << "\n";
                }
                else if (opt.bDelta)
                {
                    Color col = RenderOption::colorCoding(delta);
                    fout << "vc " << col[0] << " " << col[1] << " " << col[2] << "\n";
                }
                else if (opt.bDensity)
                {
                    Color col = RenderOption::colorCoding(rho);
                    fout << "vc " << col[0] << " " << col[1] << " " << col[2] << "\n";
                }
            }

            fout.close();
        }
    };
}

GLOBAL_NAMESPACE_END

#endif//ZML_DP_TEST_H_